import _thread
import base64
import json
import logging
import pathlib
import pickle
import threading
import time

import cv2
import flask
import loguru
import pyautogui
import pynput
import pyscreeze
import requests
import win32con
import win32gui
from flask import Flask, request

import socket

from loguru import logger

from pymouse import *
from win32gui import *

from entity.Operation import Click, Drag, Input, Operation

app = Flask(__name__)

process_status = 1


@app.route("/", methods=["GET"])
def response():
    return "ok"


@app.route("/start/<int:process_id>", methods=["GET"])
def start(process_id):
    global process_status
    process_status = 1
    thread = threading.Thread(target=local_process, args=(process_id, request.remote_addr, ))
    thread.setDaemon(True)
    thread.start()
    return str(process_id)


@app.route("/app", methods=["GET"])
def app_status():
    return str(app_inspect)


@app.route("/screenshot", methods=["GET"])
def screenshot():
    pyscreeze.screenshot("screen.jpg")
    with open("screen.jpg", "rb") as file:
        _res = file.read()
    return _res


def get_local_ip():
    # 获取计算机名称
    hostname = socket.gethostname()
    # 获取本机IP
    ip = socket.gethostbyname(hostname)
    return ip


def get_process_status(process_id, remote_address):
    url = f"http://{remote_address}:11113/process/{process_id}"
    try:
        _res = requests.get(url).text
        _res = json.loads(_res)
    except Exception:
        return -1
    if _res["code"] != "200":
        return 0
    if _res["data"][0]["project_id"] is None:
        return 0
    return 1


def get_operation(remote_address):
    url = f"http://{remote_address}:11113/em/{get_local_ip()}"
    try:
        _res = requests.get(url).json()
    except Exception:
        return -1
    return _res


def time_while(process_id, remote_address):
    global process_status
    err_number = 0
    time_op = time.time()
    while process_status:
        if app_inspect == "end":
            break
        if err_number >= 2:
            logger.error("无法连接DAS服务器")
            break
        if time.time() - time_op > 5:
            time_op = time.time()
            # 从das获取状态
            process_status = get_process_status(process_id, remote_address)
            if process_status == -1:
                err_number += 1
            continue
        time.sleep(2)
    process_status = 0


def set_log(status, message):
    _dict = {
        "info": logger.info,
        "warning": logger.warning,
        "error": logger.error
    }
    if status in _dict:
        _dict[status](message)


def execute(res):
    """执行指令"""
    values = res["data"]
    operation = values[0]
    result = values[1]
    _dict = {
        "Click": click,
        "Drag": drag,
        "Input": input
    }
    print(operation[0])
    print(operation[1], result)
    _dict[operation[0]](operation[1], result)


def click(operation, result):
    _click = Click()
    _click.__dict__ = operation
    operation = _click
    logger.info(f"[{operation.id}]:点击操作")
    if operation.translate.replace(" ", "") == "0,0":
        logger.info(f"点击{result[0]},{result[1]}")
        PyMouse().click(int(result[0]), int(result[1]))
    else:
        PyMouse().click(int(operation.translate.split(",")[0]), int(operation.translate.split(",")[1]))
        logger.info(f"点击{operation.translate.split(',')[0]},"
                    f"{operation.translate.split(',')[1]}")
    logger.info(f"延迟{operation.delay_time}秒")
    time.sleep(float(operation.delay_time))


def drag(operation, result):
    _drag = Drag()
    _drag.__dict__ = operation
    operation = _drag
    logger.info(f"[{operation.id}]:拖拽操作")
    if operation.translate.replace(" ", "") == "0,0" and \
            operation.translate_drag.replace(" ", "") == "0,0":
        logger.info(f"拖拽位置都为识别坐标，退化为点击操作")
        click(operation, result)
        return 1
    if operation.translate.replace(" ", "") == "0,0":
        operation.translate = f"{result[0]},{result[1]}"
    if operation.translate_drag.replace(" ", "") == "0,0":
        operation.translate_drag = f"{result[0]},{result[1]}"
    displacement_x = int(operation.translate_drag.split(",")[0]) - int(operation.translate.split(",")[0])
    displacement_y = int(operation.translate_drag.split(",")[1]) - int(operation.translate.split(",")[1])
    pyautogui.moveTo(int(operation.operation.split(",")[0]),
                     int(operation.operation.split(",")[1]), 0.5)
    pyautogui.dragRel(displacement_x, displacement_y, 0.5)
    logger.info(f"延迟{operation.delay_time}秒")
    time.sleep(float(operation.delay_time))


def input(operation, result):
    _input = Input()
    _input.__dict__ = operation
    operation = _input
    logger.info(f"[{operation.id}]:输入文本")
    if operation.type == "text" or "text/http":
        if operation.text == "enter" or \
                operation.text == "ENTER" or \
                operation.text == "Enter":
            logger.info(f"输入<Enter>键")
            pynput.keyboard.Controller().press(pynput.keyboard.Key.enter)
            pynput.keyboard.Controller().release(pynput.keyboard.Key.enter)
            return 1
        logger.info(f"输入{operation.text}")
        for i in operation.text:
            pynput.keyboard.Controller().press(i)
            pynput.keyboard.Controller().release(i)
            time.sleep(0.1)
        logger.info(f"延迟{operation.delay_time}秒")
        time.sleep(float(operation.delay_time))
        return 1
    if operation.type == "http":
        try:
            _res = requests.get(operation.text)
        except Exception as e:
            logger.warning(f"http请求异常：{operation.text}, {e}")
            return 0
        logger.info(f"输入：{_res.text}")
        if _res.text == "enter" or \
                _res.text == "ENTER" or \
                _res.text == "Enter":
            logger.info(f"输入<Enter>键")
            pynput.keyboard.Controller().press(pynput.keyboard.Key.enter)
            pynput.keyboard.Controller().release(pynput.keyboard.Key.enter)
            return 1
        for i in _res.text:
            pynput.keyboard.Controller().press(i)
            pynput.keyboard.Controller().release(i)
            time.sleep(0.1)
        logger.info(f"延迟{operation.delay_time}秒")
        time.sleep(float(operation.delay_time))
    logger.warning(f"输入类型错误:{operation.type},跳过...")


def local_process(process_id, remote_address):
    global process_status
    global app_inspect
    logger.warning("正在清空服务器数据..")

    app_inspect = False
    thread = threading.Thread(target=time_while, args=(process_id, remote_address,))
    thread.setDaemon(True)
    thread.start()
    err_number = 0
    while process_status:
        _res = get_operation(remote_address)
        if _res == -1:
            err_number += 1
            if err_number >= 3:
                logger.error("获取指令超时，结束任务")
                break
        # 获取到指令，执行指令
        if _res["code"] == "10000":
            execute(_res)
            continue
        # 服务器无法访问本地进程
        if _res["code"] == "10001":
            logger.error("服务器无法访问本地进程")
            break
        # 正常退出
        if _res["code"] == "10002":
            logger.info("正常退出")
            break
        # 暂时没有指令，休眠1s
        if _res["code"] == "11001":
            time.sleep(1)
            continue
        else:
            print(_res)
        # 11002,打印日志
        if _res["code"] == "11002":
            logger.info(_res["message"])
            continue
        # 11003，本地没有资源
        if _res["code"] == "11003":
            logger.error(_res["message"])
            break
        # 11004，获取项目应用程序名称
        if _res["code"] == "11004":
            app_name = _res["message"]
            resolving_power = _res["data"]
            logger.info(f"已拿到应用程序句柄名，打开<{app_name}>应用程序监视进程，分辨率：{resolving_power}")
            _thread.start_new_thread(inspect_app, (app_name, resolving_power, ))
            continue
    app_inspect = "end"
    logger.info("任务关闭")


def init():
    project_path = pathlib.Path(".").absolute()
    log_path = project_path / "log"
    logger.info("本地进程已启动，日志输出至 >>> " + str(log_path))
    if not log_path.is_dir():
        log_path.mkdir()


def get_apps():
    """获取应用程序进程列表"""
    list_lpWindowName = []
    titles = set()

    def foo(hwnd, nouse):
        # 去掉下面这句就所有都输出了，但是我不需要那么多
        if IsWindow(hwnd) and IsWindowEnabled(hwnd) and IsWindowVisible(hwnd):
            titles.add(GetWindowText(hwnd))

    EnumWindows(foo, 0)
    lt = [t for t in titles if t]
    lt.sort()
    for t in lt:
        list_lpWindowName.append(t)
    return list_lpWindowName


def move_app(app_name, resolving_power):
    """根据分辨率移动并调整窗口"""
    hwnd = win32gui.FindWindow(None, app_name)
    win32gui.SetWindowPos(hwnd, win32con.HWND_TOPMOST, 0, 0, int(resolving_power.split("x")[0]),
                          int(resolving_power.split("x")[1]), win32con.SWP_SHOWWINDOW)


def inspect_app(app_name, resolving_power):
    global app_inspect
    while 1:
        if app_inspect == "end":
            break
        if app_name not in get_apps():
            time.sleep(1)
            continue
        move_app(app_name, resolving_power)
        logger.warning("本地进程已检测到窗口，把窗口调整为对应分辨率并且置顶")
        app_inspect = True
        break
    while 1:
        if app_inspect == "end":
            break
        if app_name not in get_apps():
            app_inspect = False
            break
        time.sleep(2)


if __name__ == '__main__':
    app_inspect = False
    init()
    logger.add("log/log", rotation="10 MB")
    log = logging.getLogger('werkzeug')
    log.setLevel(logging.ERROR)
    app.run(host="0.0.0.0", port=11114, debug=True)

